import MapboxDraw, { type DrawModes } from '@mapbox/mapbox-gl-draw';
import type { LngLat } from 'mapbox-gl';
import type Mapless from '..';
import { AngleTool } from './angle';
import { AreaTool } from './area';
import { LineTool } from './line';
import { PointTool } from './point';
import { PositionTool } from './position';
import themeStyles from './theme';
import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css';

interface DrawingModes extends DrawModes {
  DRAW_ANGLE: 'draw_angle';
  LNGLAT_SELECT: 'lnglat_select';
}

type DrawingMode = DrawingModes[keyof DrawingModes];

// TODO 需要优化
class Draw {
  public map: mapboxgl.Map | null;
  public mapless: Mapless | undefined;

  public drawControl: MapboxDraw | undefined;
  public drawingMode: DrawingMode = 'simple_select';

  private pointTool: PointTool | undefined;
  private lineTool: LineTool | undefined;
  private areaTool: AreaTool | undefined;
  private angleTool: AngleTool | undefined;
  private positionTool: PositionTool | undefined;

  constructor(mapless: Mapless) {
    this.mapless = mapless;
    this.map = this.mapless.map;
    // 初始化绘图工具
    if (this.map) {
      this.drawControl = new MapboxDraw({
        displayControlsDefault: false,
        clickBuffer: 5,
        userProperties: true,
        // controls: {
        //   point: true,
        //   line_string: true,
        //   polygon: true,
        //   trash: true,
        //   combine_features: false,
        //   uncombine_features: false,
        // },
        // TODO DrawCustomModes 是一个对象，用于定义自定义绘图模式。
        // 尝试用这个绘制不同的图形，如圆、角、椭圆、矩形等。
        // modes: {
        //   "draw_angle":DrawAngle : DrawCustomMode
        //   "draw_circle":DrawCircle : DrawCustomMode
        // }
        // \mapbox-gl-draw-main\src\modes\draw_line_string.js
        styles: themeStyles,
      });
      this.map.addControl(this.drawControl);

      this.map.on('draw.create', this.createMeasure);
      this.map.on('draw.update', this.updateMeasure);
      this.map.on('draw.modechange', (e) => {
        const em = e as { mode: string };
        if (em?.mode === 'simple_select') {
          this.onDrawOver?.();
        }
      });
      this.map.on('draw.delete', this.deleteMeasure);

      this.pointTool = new PointTool(this);
      this.lineTool = new LineTool(this);
      this.areaTool = new AreaTool(this);
      this.angleTool = new AngleTool(this);
      this.positionTool = new PositionTool(this);
    }
  }

  onDrawOver = () => {};

  createMeasure = (e: unknown) => this.updateMeasure(e, true);

  updateMeasure = (e: unknown, isNew = false) => {
    const evt = e as { features: GeoJSON.Feature[] };
    const feature = evt.features[0];
    if (!feature) return;
    // 清除旧的标注
    if (!isNew) {
      this.clearMeasure(`dzmap-draw-label-${feature.id}`);
    }
    const getGeometryHandler = {
      // TODO Position也走此处Point的处理~
      Point: (feature: GeoJSON.Feature) =>
        this.pointTool?.handleCreate(feature),
      LineString: (feature: GeoJSON.Feature) => {
        if (this.angleTool?.validate(feature)) {
          this.angleTool?.handleCreate(feature);
        } else {
          this.lineTool?.handleCreate(feature);
        }
      },
      Polygon: (feature: GeoJSON.Feature) =>
        this.areaTool?.handleCreate(feature),
    };
    const { type } = feature.geometry;
    // TODO ??? 为什么这里要判断 type 类型，而不是直接使用 getGeometryHandler[type]?
    if (type !== 'LineString' && type !== 'Polygon' && type !== 'Point') return;
    const handler = getGeometryHandler[type];
    handler?.(feature);
  };

  deleteMeasure = (e: unknown) => {
    const evt = e as { features: GeoJSON.Feature[] };
    const feature = evt.features[0];
    if (!feature) return;
    this.clearMeasure(`dzmap-draw-label-${feature.id}`);

    this.pointTool?.handleDelete(feature);
    this.lineTool?.handleDelete(feature);
    this.areaTool?.handleDelete(feature);
    this.angleTool?.handleDelete(feature);
  };

  setDrawingMode(mode: DrawingMode) {
    this.drawingMode = mode;
    this.onDrawOver?.();
  }

  clearMeasure(id: string) {
    if (!this.map) return;
    try {
      this.map.removeLayer(id);
      this.map.removeSource(id);
    } catch (error) {
      console.log(error);
    }
  }

  startDrawing(mode: DrawMode) {
    if (!this.drawControl) return;
    const handler = {
      point: this.drawPoint,
      line_string: this.drawLine,
      polygon: this.drawPolygon,
      angle: this.drawAngle,
    };
    handler[mode]?.();
  }

  cancelDrawing() {
    if (!this.drawControl) return;
    // 恢复到简单选择模式
    this.drawingMode = 'simple_select';
    this.drawControl.changeMode('simple_select');
    // 恢复原始光标
    if (this.map) {
      this.map.getCanvas().style.cursor = '';
    }
  }

  private drawPoint = () => {
    this.drawingMode = 'draw_point';
    this.drawControl?.changeMode('draw_point');
  };

  private drawLine = () => {
    this.drawingMode = 'draw_line_string';
    this.drawControl?.changeMode('draw_line_string');
  };

  private drawPolygon = () => {
    this.drawingMode = 'draw_polygon';
    this.drawControl?.changeMode('draw_polygon');
  };

  private drawAngle = () => {
    this.drawingMode = 'draw_angle';
    this.drawControl?.changeMode('draw_line_string');
  };

  deleteDraw = () => {
    if (!this.drawControl) return;
    const _ids = this.drawControl.getSelectedIds();
    if (_ids?.length === 0) return;
    const colls = this.drawControl.getSelected();
    const notPtn =
      colls.features.length > 0 && colls.features[0].geometry.type !== 'Point';
    notPtn && this.clearMeasure(`dzmap-draw-label-${_ids[0]}`);
    this.drawControl.delete(_ids);
  };

  clearDrawings() {
    if (!this.drawControl) return;
    const featColl = this.drawControl.getAll();
    featColl.features.forEach((feature) => {
      this.clearMeasure(`dzmap-draw-label-${feature.id}`);
      this.drawControl?.delete(feature.id as string);
    });
    this.positionTool?.clearLayers();
  }

  startPointPickingAsync = (): Promise<[LngLat?, Error?]> => {
    if (this.positionTool) {
      return this.positionTool.startPointPickingAsync();
    }
    return Promise.resolve([undefined, new Error('positionTool 未初始化')]);
  };

  startPointPicking() {
    return this.positionTool?.startPointPicking();
  }

  destroy() {
    try {
      this.drawControl && this.map?.removeControl(this.drawControl);
      this.drawControl = undefined;
    } catch (_error) {
      // console.log(error);
    }
  }
}

export default Draw;

export type DrawMode = 'point' | 'line_string' | 'polygon' | 'angle';
